<?php

/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/2/19
 * Time: 20:57
 */
class Route
{
    /**
     * @var string 路由规则前缀，上一次的分组规则名
     * */
    private static $RulePrefix = '';
    private static $route_type = 1;

    /**
     * @var string 路由指向的类前缀
     * */
    private static $classPrefix = '';

    /**
     * @var string 上一次的路由规则名
     * */
    private static $lastRouteName = null;

    /**
     * @var array 实际请求的路由相关信息，请求类型，执行路径等
     * */
    private static $RealRequestData;

    /** @var string 非命名空间的前面的绝对路径，用于拼接加载类 */
    private static $RootPath = null;

    /** @var string 是否有命名空间 */
    private static $IsNamespace = false;

    /** @var array 路由规则储存 */
    private static $RuleAddress = array(
        //cs:md5(请求的路径) 0下标:实际指向的路径 1下标：请求方式 2下标：客户端请求的路径规则
        //'cs' => array('app\conent\conent.php@index', 'get', 'cs'),
    );

    /** @var array  路由的规则$RuleAddress当前路由保存的数据 */
    private static $urlData = null;

    /** @var array 路由分组的拦截器 */
    private static $GroupStop = array();

    /**
     * @var array 用户配置储存
     * */
    private static $ClientConfig = array(
        'controller_suffix' => '.php'//控制器后缀
    );

    /**
     * @var Closure 自定义路由匹配失败事件 2c1567a88893458d66fcbdd58df03f10
     * */
    public static $EventNotFound = null;//自定义路由匹配失败事件

    /**
     * @var Closure 有命名空间的类自动加载事件
     * */
    public static $EventAutoloadClass = null;
    /**
     * @var Closure 编辑路由规则事件，在路由转发之前可以修改路由 2c1567a88893458d66fcbdd58df03f10
     * */
    private static $EventEditUrl = null;

    private static $instance;

    //防止使用new直接创建对象
    private function __construct()
    {
    }

    //防止使用clone克隆对象
    private function __clone()
    {
    }

    //获取当前类对象
    public static function getInstance()
    {
        //判断$instance是否是Singleton的对象，不是则创建
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    //初始化配置
    public static function init(array $config = array())
    {
        if (is_array($config)) {
            if (empty($config['root_path'])) exit('项目根地址不可为空：root_path');
            self::$RootPath = $config['root_path'];//根路径
            if (isset($config['is_namespace']) && is_bool($config['is_namespace'])) {
                //是否有命名空间，有命名空间，就直走命名空间的
                self::$IsNamespace = $config['is_namespace'];
            }
            self::SetAutoloadClass();//注册自动加载，虽然自动加载使用过的无命名空间的，但是找不到类会尝试自动加载，自动加载也没有就报错
            if (!empty($config['controller_suffix'])) {//控制器后缀
                self::$ClientConfig['controller_suffix'] = $config['controller_suffix'];
            }
            if (isset($config['route_type'])) {//路由转发模式 1:伪静态，0：mca模式
                self::$route_type = $config['route_type'];
            }
        }
        return self::getInstance();
    }

    /**
     * @param $rule_path string 路由地址
     * @return 自动加载route所有路由文件
     */
    public static function auto_load_route($rule_path)
    {
        $rule_path = $rule_path . DIRECTORY_SEPARATOR . '*.route.php';
        $route_list = glob($rule_path);
        if (empty($route_list)) return false;
        foreach ($route_list as $route) {
            if (file_exists($route)) include $route;
        }
        return true;
    }

    /**
     * @return  获取所有路由
     * */
    public static function showAllRule()
    {
        return self::$RuleAddress;
    }

    /**
     * @return   设置类的自动加载
     * */
    private static function SetAutoloadClass()
    {
        //注册自动加载,注册当前类的_autoload_class方法为自动加载
        spl_autoload_register(array(__CLASS__, '_autoload_class'));
    }

    /**
     * @access  类的自动加载
     * @param string $className
     * */
    private static function _autoload_class($className)
    {
        if (!is_null(self::$EventAutoloadClass)) {
            //用户自定义写自动加载代码用
            $EventAutoloadClass = self::$EventAutoloadClass;
            $EventAutoloadClass($className);//执行自动加载事件
        } else {
            $class_path = str_replace('\\', '/', self::$RootPath . $className) . self::$ClientConfig['controller_suffix'];
            if (file_exists($class_path)) include $class_path;
        }
    }

    /**
     * @param $function  自定义类的自动加载
     * */
    public static function SetAutoloadEvent($function)
    {
        self::$EventAutoloadClass = $function;
    }

    //设置请求路由，自定义请求类型
    public static function map($rule, $path, $request_type)
    {
        self::AddRule($rule, $path, strtoupper($request_type));//strtoupper把请求类型转为大写
        return self::getInstance();
    }

    //设置请求路由，自定义请求类型
    public static function all($rule, $path)
    {
        self::AddRule($rule, $path, 'ALL');
        return self::getInstance();
    }

    //设置get请求路由
    public static function get($rule, $path)
    {
        self::AddRule($rule, $path, 'GET');
        return self::getInstance();
    }

    //设置post请求路由
    public static function post($rule, $path)
    {
        self::AddRule($rule, $path, 'POST');
        return self::getInstance();
    }

    //路由分组
    public static function group($rulePrefix, $classPrefix, $ruleEvent)
    {
        self::$RulePrefix = $rulePrefix;
        self::$classPrefix = $classPrefix;
        $ruleEvent();
        self::$RulePrefix = '';
        self::$classPrefix = '';
        return self::getInstance();
    }

    //添加路由
    private static function AddRule($rule, $path, $request_type)
    {
        if (!$path) msg_no('路由path参数不可为空', '路由定义错误');
        //不是闭包的拼路径
        if (!$path instanceof Closure) {
            //如果设定了前缀绝对路径，就是用绝对路径解析，如果没有就直接用（自动加载）
            if (self::$IsNamespace == true) {
                $path = self::$classPrefix . $path;
            } else {
                $path = self::$RootPath . self::$classPrefix . $path;
            }
        }
        $rule = self::$RulePrefix . $rule;
        self::$lastRouteName = $rule_md5 = 'WEK' . md5($rule);
        self::$RuleAddress[$rule_md5] = array(
            'rule' => $rule,//请求规则
            'path' => $path,//实际地址
            'request_type' => $request_type,//支持的请求类型
        );
        if (self::$RulePrefix) {//路由所属分组
            self::$RuleAddress[$rule_md5]['group'] = 'group_' . md5(self::$RulePrefix);
        }
    }

    /**
     * @param $stop_info 参数
     * @return 路由拦截器:拦截器记录到路由里面
     * */
    public function RouteStop($stop_info)
    {
        //拦截器保存到路由中
        self::$RuleAddress[self::$lastRouteName]['route_stop'] = $stop_info;
//        dd(self::$RuleAddress);
        return self::getInstance();
    }

    /**
     * @param $stop_info 参数
     * @return 路由分组拦截器:拦截器记录到分组中间级
     * */
    public function GroupStop($stop_info)
    {
        //保存到分组拦截器中
        $group_stop = 'group_' . md5(self::$RulePrefix);
        self::$GroupStop[$group_stop] = $stop_info;
//        dd(self::$RuleAddress);

        return self::getInstance();
    }

    //拦截器
    private static function analysisStop()
    {
        base::load_sys_class('Stop', '', 0);//加载拦截器
        //1:拦截器优先级 路由拦截器 > 分组路由的拦截器
        if (isset(self::$urlData['route_stop']) && !empty(self::$urlData['route_stop'])) {
            $route_stop = self::$urlData['route_stop'];
            //路由拦截器
            if ($route_stop instanceof Closure) {
                $route_stop();
            } else {
                //验证拦截器,路由中只负责调用拦截器，怎么处理交给拦截器 group_prefix
                Stop::ChckStop($route_stop);
            }
        }
        if (isset(self::$urlData['group']) && isset(self::$GroupStop[self::$urlData['group']])) {
            $group_stop = self::$GroupStop[self::$urlData['group']];
            if (isset($group_stop)) {
                if ($group_stop instanceof Closure) {
                    $group_stop();
                } else {
                    //验证拦截器,路由中只负责调用拦截器，怎么处理交给拦截器 group_prefix
                    Stop::ChckStop($group_stop);
                }
            }
        }
    }

    /**
     * @param $path 实际自定义的路由地址
     * @return  没命名空间的路由转发实现
     * */
    private static function goNoNamespace($path)
    {
        $data = explode('@', $path);
        $class = $data[0] . self::$ClientConfig['controller_suffix'];//类文件地址
//        dd($data);
        $route_c = basename($data[0]);//控制器名
        $route_m = str_replace(LINE . $route_c, '', str_replace(ROOT_PATH . 'app' . LINE . 'modules' . LINE, '', $data[0]));//模块名
        $route_a = $data[1];//方法名
//        dd($route_c);
        define('ROUTE_M', $route_m);//$route_m
        define('ROUTE_C', $route_c);//$route_c
        define('ROUTE_A', $route_a);//$route_a
//        dd($class);
        if (!is_file($class)) self::NotFound(array($class, '路由指向的控制器不存在.'));
        include $class;
        $object = new $route_c();//没有命名空间的加载，如果有就直接实例化，如果加载失败，尝试自动加载，自动加载失败了就真的失败了
        if (method_exists($object, $route_a)) {
            $object->$route_a();
            exit();
        }
        self::NotFound(array($class . '中：' . $route_a . '()', '路由定义的方法不存在'));
    }

    /**
     * @param $path 实际自定义的路由地址
     * @return  有命名空间的路由转发实现
     * */
    public static function goNamespace($path)
    {
        //有命名空间的路由转发实现
        $data = explode('@', $path);
        $class = $data[0];
        $function = $data[1];//方法名
        $object = new $class();
        if (method_exists($object, $function)) {
            $object->$function();
            exit();
        }
        self::NotFound(array($class . '中：' . $function . '()', '路由定义的方法不存在'));
    }

    /**
     * @return 路由转发
     * */
    public static function go()
    {
        if (is_null(self::$RootPath)) exit('请执行初始化init方法');
        self::analysisClient();//分析客户端信息
        self::analysisRule();//对比路由是否存在可以访问
        self::analysisStop();//拦截器
        $path = self::$RealRequestData['path'];
        if ($path instanceof Closure) {
            $path();
            exit();
        }
        //请求转发到实际地址
        if (self::$IsNamespace == true) {//命名空间自动加载
            self::goNamespace($path);
        } else {//没有命名空间的框架，使用设置的绝对路径
            self::goNoNamespace($path);
        }
    }

    /**
     * @$function Closure 设置自定义执行事件
     * @return 路由找不到自定义事件
     * */
    public static function SetNotFound(Closure $function)
    {
        self::$EventNotFound = $function;
    }

    //执行自定义错误事件
    private static function NotFound($errInfo)
    {
        if (!is_null(self::$EventNotFound)) {
            $NotFound = self::$EventNotFound;
            $NotFound(array('title' => $errInfo[0], 'info' => $errInfo[1]));
            exit();
        }
        header('HTTP/1.1 404 Not Found');
        header('Status: 404 Not Found');
        exit('not found');
    }

    //处理请求url
    public static function analysisUrl($url_param)
    {
        if (empty($url_param) || in_array($url_param, array(null, NULL))) {
            //没有带参数来，默认首页
            $url_param = '/';
        } elseif ($url_param != '/') {
            $url_param = ltrim($url_param, "/");//只要不是首页的，就把左侧的/去除
            $url_param = rtrim($url_param, '/');//只要不是首页的，去除右侧的 /
        }
        $_SERVER['ajax'] = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower(trim($_SERVER['HTTP_X_REQUESTED_WITH'])) == 'xmlhttprequest') || input('ajax');
        self::$ClientConfig['ip'] = ip();
        self::$ClientConfig['url_rule'] = $url_param;
    }

    //分析客户端数据
    private static function analysisClient()
    {
        self::$ClientConfig['request_type'] = $_SERVER['REQUEST_METHOD'];
        if (self::$route_type != 1) {
            $route_m = 'admin';//模型
            $route_c = 'admin';//控制器
            $route_a = 'index';//方法
            //http://wekcms.cc/?m=content&c=content&a=index
            if (!empty($_GET['m'])) $route_m = strtolower($_GET['m']);
            if (!empty($_GET['c'])) $route_c = strtolower($_GET['c']);
            if (!empty($_GET['a'])) $route_a = strtolower($_GET['a']);
            define('ROUTE_M', $route_m);
            define('ROUTE_C', $route_c);
            define('ROUTE_A', $route_a);
            $path = self::$RootPath . 'app' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $route_m . DIRECTORY_SEPARATOR . $route_c . self::$ClientConfig['controller_suffix'];
            if (file_exists($path)) {
                include $path;
                $object = new $route_c;//没有命名空间的加载，如果有就直接实例化，如果加载失败，尝试自动加载，自动加载失败了就真的失败了
                if (method_exists($object, $route_a)) {
                    $object->$route_a();
                    exit();
                }
                self::NotFound(array('方法：' . $route_a, '执行方法不存在'));
            }
            self::NotFound(array($path, '控制器文件不存在'));
        }
        self::$ClientConfig['url_rule'] = false;//初始化
        // index.php/wek的无法识别
        //兼容过滤
        //有就赋值，多做几个兼容判断，最后判断有没有值，没有就给默认的
        if (isset($_SERVER['REQUEST_URI'])) {
            $_SERVER['REQUEST_URI'] = str_replace('/index.php?', '', $_SERVER['REQUEST_URI']); // 兼容 iis6
            $_SERVER['REQUEST_URI'] = str_replace('/index.php', '', $_SERVER['REQUEST_URI']); // 兼容 iis6
            return self::analysisUrl($_SERVER['REQUEST_URI']);
        }
        if (self::$ClientConfig['url_rule'] == false) {
            if (isset($_GET['s'])) {
                if (isset($_GET['s'])) $_GET['s'] = str_replace('/index.php?', '', $_GET['s']); // 兼容 iis6
                if (isset($_GET['s'])) $_GET['s'] = str_replace('/index.php', '', $_GET['s']); // 兼容 iis6
                return self::analysisUrl($_GET['s']);
            }
        }
        if (self::$ClientConfig['url_rule'] == false) {
            if (isset($_SERVER['REDIRECT_URL'])) {
                if (isset($_SERVER['REDIRECT_URL'])) $_SERVER['REDIRECT_URL'] = str_replace('/index.php?', '', $_SERVER['REDIRECT_URL']); // 兼容 iis6
                if (isset($_SERVER['REDIRECT_URL'])) $_SERVER['REDIRECT_URL'] = str_replace('/index.php', '', $_SERVER['REDIRECT_URL']); // 兼容 iis6
                return self::analysisUrl($_SERVER['REDIRECT_URL']);
            }
        }
        //最后还是false
        if (self::$ClientConfig['url_rule'] == false) {
            //实在不行了，转到mca模式访问默认未首页，并给客户端标记未mca模式
            self::$ClientConfig['ip'] = ip();
            self::$ClientConfig['url_rule'] = '/';
        }
    }

    //接受到用户请求，获取到路由规则之后，自定义修改时间，用于携带的参数的分离
    public static function EditUrl($function)
    {
        self::$EventEditUrl = $function;
    }

    private static function SetUrl()
    {
        $editUrl = self::$EventEditUrl;
        $editUrl(self::$ClientConfig['url_rule']);
    }

    //解析客户端请求路由对应的路由数据，并判断
    private static function analysisRule()
    {
        if (!is_null(self::$EventEditUrl) && self::$EventEditUrl instanceof Closure) {
            self::SetUrl();
        }
        //路由带的参数
        self::$ClientConfig['url_rule'] = str_replace('.html', '', self::$ClientConfig['url_rule']);
        $url_arr = explode('-', self::$ClientConfig['url_rule']);
        $count = count($url_arr);
//        dd($url_arr);
        if ($count > 1) {//带参数了
            foreach ($url_arr as $key => $value) {
                if ($key == 0) {
                    self::$ClientConfig['url_rule'] = str_replace('/', '', $url_arr[0]);
                    continue;
                }
                //                $url_para = 'url_' . $key;
                $_GET['url_' . $key] = $value;
            }
        }
        $rule_md5 = 'WEK' . md5(self::$ClientConfig['url_rule']);
        $msg = '';
        $title = '路由未定义';
//        dd(self::$RuleAddress);
        if (isset(self::$RuleAddress[$rule_md5])) {
            self::$urlData = self::$RuleAddress[$rule_md5];
            $request_type = self::$ClientConfig['request_type'];
            self::$RealRequestData = self::$RuleAddress[$rule_md5];
            //路由规则 GET POST   GET,POST  多种  all所有
//            dv($request_type);
//            dd(self::$urlData['request_type']);
            $route_request_type = explode(',', self::$urlData['request_type']);
            if (in_array($request_type, $route_request_type) || self::$urlData['request_type'] == 'ALL') {//只有map可以定义多个有效请求类型,其他的直接就赋值给了self::$urlData['request_type'],如果self::$urlData['request_type']是all不管什么请求类型都放行了
                return true;
            }
            $title = '请求类型和路由不匹配';
            $msg = '<br//>' . '路由设置的请求类型为：【' . self::$ClientConfig['request_type'] . '】' . '<br//>';
            $msg .= '当前请求的类型为：【' . self::$ClientConfig['request_type'] . '】' . '<br//>';
        }
        $msg .= '请求的路由规则为：【' . self::$ClientConfig['url_rule'] . '】' . '<br//>';
        msg_no($msg, $title);
    }
}